#include "linux/irq.h"
#include "linux/slab.h"
#include "linux/irqdesc.h"
#include "linux/printk.h"
#include "linux/errno.h"
#include "linux/of_irq.h"
#include "linux/of.h"
#include "linux/irqdomain.h"
#include "linux/irqchip.h"
#include "linux/list.h"
#include "linux/generic-chip.h"

static LIST_HEAD(gc_list);
//static DEFINE_RAW_SPINLOCK(gc_lock);

void irq_reg_writel(struct irq_chip_generic *gc, u32 val, int reg_offset)
{
    if (gc->reg_writel)
        gc->reg_writel(val, gc->reg_base + reg_offset);
    else
        writel(val, gc->reg_base + reg_offset);
}

u32 irq_reg_readl(struct irq_chip_generic *gc, int reg_offset)
{
    if (gc->reg_readl)
        return gc->reg_readl(gc->reg_base + reg_offset);
    else
        return readl(gc->reg_base + reg_offset);
}

void irq_gc_noop(struct irq_data *d)
{

}

void irq_gc_mask_disable_reg(struct irq_data *d)
{
    struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
    struct irq_chip_type *ct = irq_data_get_chip_type(d);
    u32 mask = d->mask;

    irq_gc_lock(gc);
    irq_reg_writel(gc, mask, ct->regs.disable);
    *ct->mask_cache &= ~mask;
    irq_gc_unlock(gc);
}

/**
 * irq_gc_mask_set_bit - Mask chip via setting bit in mask register
 * @d: irq_data
 *
 * Chip has a single mask register. Values of this register are cached
 * and protected by gc->lock
 */
void irq_gc_mask_set_bit(struct irq_data *d)
{
    struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
    struct irq_chip_type *ct = irq_data_get_chip_type(d);
    u32 mask = d->mask;

    irq_gc_lock(gc);
    *ct->mask_cache |= mask;
    irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask);
    irq_gc_unlock(gc);
}

void irq_gc_mask_clr_bit(struct irq_data *d)
{
    struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
    struct irq_chip_type *ct = irq_data_get_chip_type(d);
    u32 mask = d->mask;

    irq_gc_lock(gc);
    *ct->mask_cache &= ~mask;
    irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask);
    irq_gc_unlock(gc);
}

void irq_gc_unmask_enable_reg(struct irq_data *d)
{
    struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
    struct irq_chip_type *ct = irq_data_get_chip_type(d);
    u32 mask = d->mask;

    irq_gc_lock(gc);
    irq_reg_writel(gc, mask, ct->regs.enable);
    *ct->mask_cache |= mask;
    irq_gc_unlock(gc);
}

void irq_gc_ack_set_bit(struct irq_data *d)
{
    struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
    struct irq_chip_type *ct = irq_data_get_chip_type(d);
    u32 mask = d->mask;

    irq_gc_lock(gc);
    irq_reg_writel(gc, mask, ct->regs.ack);
    irq_gc_unlock(gc);
}

void irq_gc_ack_clr_bit(struct irq_data *d)
{
    struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
    struct irq_chip_type *ct = irq_data_get_chip_type(d);
    u32 mask = ~d->mask;

    irq_gc_lock(gc);
    irq_reg_writel(gc, mask, ct->regs.ack);
    irq_gc_unlock(gc);
}

void irq_gc_mask_disable_reg_and_ack(struct irq_data *d)
{
    struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
    struct irq_chip_type *ct = irq_data_get_chip_type(d);
    u32 mask = d->mask;

    irq_gc_lock(gc);
    irq_reg_writel(gc, mask, ct->regs.mask);
    irq_reg_writel(gc, mask, ct->regs.ack);
    irq_gc_unlock(gc);
}

void irq_gc_eoi(struct irq_data *d)
{
    struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
    struct irq_chip_type *ct = irq_data_get_chip_type(d);
    u32 mask = d->mask;

    irq_gc_lock(gc);
    irq_reg_writel(gc, mask, ct->regs.eoi);
    irq_gc_unlock(gc);
}

int irq_gc_set_wake(struct irq_data *d, unsigned int on)
{
    struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
    u32 mask = d->mask;

    if (!(mask & gc->wake_enabled))
        return -EINVAL;

    irq_gc_lock(gc);
    if (on)
        gc->wake_active |= mask;
    else
        gc->wake_active &= ~mask;
    irq_gc_unlock(gc);
    return 0;
}

static void irq_init_generic_chip(struct irq_chip_generic *gc,char *name,
                      int num_ct, unsigned int irq_base,
                      void __iomem *reg_base, irq_flow_handler_t handler)
{
    //raw_spin_lock_init(&gc->lock);
    gc->num_ct = num_ct;
    gc->irq_base = irq_base;
    gc->reg_base = reg_base;
    gc->chip_types->chip.name = name;
    gc->chip_types->handler = handler;
}

struct irq_chip_generic * irq_alloc_generic_chip(char *name, int num_ct, unsigned int irq_base, void __iomem *reg_base, irq_flow_handler_t handler)
{
    struct irq_chip_generic *gc;
    unsigned long sz = sizeof(*gc) + num_ct * sizeof(struct irq_chip_type);

    gc = kzalloc(sz, GFP_KERNEL);
    if (gc)
    {
        irq_init_generic_chip(gc, name, num_ct, irq_base, reg_base, handler);
    }
    return gc;
}

void irq_setup_generic_chip(struct irq_chip_generic *gc, u32 msk,
                            enum irq_gc_flags flags, unsigned int clr,
                            unsigned int set)
{
    struct irq_chip_type *ct = gc->chip_types;
    struct irq_chip *chip = &ct->chip;
    unsigned int i;

    //raw_spin_lock(&gc_lock);
    list_add_tail(&gc->list, &gc_list);
    //raw_spin_unlock(&gc_lock);

    //irq_gc_init_mask_cache(gc, flags);

    for (i = gc->irq_base; msk; msk >>= 1, i++)
    {
        if (!(msk & 0x01))
            continue;

        //if (flags & IRQ_GC_INIT_NESTED_LOCK)
            //irq_set_lockdep_class(i, &irq_nested_lock_class);

        if (!(flags & IRQ_GC_NO_MASK))
        {
            struct irq_data *d = irq_get_irq_data(i);

            if (chip->irq_calc_mask)
                chip->irq_calc_mask(d);
            else
                d->mask = 1 << (i - gc->irq_base);
        }
        irq_set_chip_and_handler(i, chip, ct->handler);
        irq_set_chip_data(i, gc);
        irq_modify_status(i, clr, set);
    }
    gc->irq_cnt = i - gc->irq_base;
}